package com.hhkj.study.api.user.sevice.biz.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.hhkj.study.admin.course.entity.CourseAssocResourceStat;
import com.hhkj.study.admin.course.entity.CourseStructure;
import com.hhkj.study.admin.course.enums.CourseAssocResourceTypeEnum;
import com.hhkj.study.admin.course.model.podo.CoursePo;
import com.hhkj.study.admin.course.model.res.CourseAssocResourceStatRes;
import com.hhkj.study.admin.course.model.res.CourseRes;
import com.hhkj.study.admin.course.service.biz.CourseBizService;
import com.hhkj.study.admin.course.service.extend.CourseExtendService;
import com.hhkj.study.admin.goods.entity.Goods;
import com.hhkj.study.admin.goods.model.podo.GoodsSkuPo;
import com.hhkj.study.admin.goods.service.base.GoodsService;
import com.hhkj.study.admin.goods.service.base.GoodsSkuService;
import com.hhkj.study.admin.order.model.podo.OrderItemDo;
import com.hhkj.study.admin.order.model.podo.OrderItemPo;
import com.hhkj.study.admin.order.service.base.OrderItemService;
import com.hhkj.study.admin.resource.entity.ResourceCourseBase;
import com.hhkj.study.admin.resource.entity.ResourceQuestionBank;
import com.hhkj.study.admin.resource.model.podo.ResourceCourseLiveDo;
import com.hhkj.study.admin.resource.model.podo.ResourceCourseLivePo;
import com.hhkj.study.admin.resource.model.podo.ResourceCourseVideoPo;
import com.hhkj.study.admin.resource.service.base.ResourceCourseLiveService;
import com.hhkj.study.admin.resource.service.extend.ResourceCourseVideoExtendService;
import com.hhkj.study.admin.resource.service.extend.ResourceQuestionBankExtendService;
import com.hhkj.study.admin.user.entity.User;
import com.hhkj.study.admin.user.entity.UserExercise;
import com.hhkj.study.admin.user.entity.UserListen;
import com.hhkj.study.admin.user.service.base.UserExerciseService;
import com.hhkj.study.admin.user.service.base.UserListenService;
import com.hhkj.study.admin.user.service.extend.UserExtendService;
import com.hhkj.study.api.user.model.req.ApiCustomerUserCourseBaseReq;
import com.hhkj.study.api.user.model.res.ApiCustomerUserCourseRes;
import com.hhkj.study.api.user.model.res.ApiCustomerUserStudyReportRes;
import com.hhkj.study.api.user.model.res.ApiUserCourseStudyRecordRes;
import com.hhkj.study.api.user.sevice.biz.ApiUserCourseBizService;
import com.hhkj.study.common.cache.GlobalCache;
import com.hhkj.study.common.model.req.CourseVideoReq;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.cont.SysConst;
import com.ruoyi.common.enums.DictCodeEnum;
import com.ruoyi.common.exception.GlobalException;
import com.ruoyi.common.model.dto.OutStudentCourseDTO;
import com.ruoyi.common.model.dto.OutStudentDTO;
import com.ruoyi.common.thirdparty.train.TrainApiClient;
import com.ruoyi.framework.login.api.ApiSessionHolder;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.commons.lang3.tuple.Triple;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.*;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;

/**
 * @Author luowu
 * @Date 2024/10/12 涵 11:57:46
 */
@Slf4j
@Service
public class ApiUserCourseBizServiceImpl implements ApiUserCourseBizService {

    @Resource
    private OrderItemService orderItemService;

    @Resource
    private GoodsSkuService goodsSkuService;

    @Resource
    private CourseBizService courseBizService;

    @Resource
    private CourseExtendService courseExtendService;

    @Resource
    private TrainApiClient trainApiClient;

    @Resource
    private GoodsService goodsService;

    @Resource
    private UserExtendService userExtendService;

    @Resource
    private ResourceQuestionBankExtendService questionBankExtendService;

    @Resource
    private UserListenService userListenService;

    @Resource
    private UserExerciseService userExerciseService;

    @Resource
    private GlobalCache globalCache;

    @Resource
    private ResourceCourseVideoExtendService videoExtendService;

    @Resource
    private ResourceCourseLiveService liveService;

    @Override
    public ApiCustomerUserCourseRes userCourseByCourseId(Long userId, Long courseId, Long orderItemId) {
        OrderItemPo orderItemPo = orderItemService.getPoById(orderItemId);
        GoodsSkuPo goodSku = goodsSkuService.getPoById(orderItemPo.getGoodsSkuId());
        CourseRes courseRes = courseBizService.getById(courseId);
        if (ObjectUtil.isNull(courseRes)) {
            throw new GlobalException("用户所购买课程不存在");
        }
        ApiCustomerUserCourseRes userCourseRes = BeanUtil.toBean(courseRes, ApiCustomerUserCourseRes.class);
        CourseAssocResourceStat courseAssocResourceStat = courseExtendService.getCourseAssocResourceStat(courseId, CourseAssocResourceTypeEnum.VIDEO);
        userCourseRes.setValidStartTime(goodSku.getValidStartTime());
        userCourseRes.setValidEndTime(goodSku.getValidEndTime());
        // 视频类型枚举值,默认统计课时的视频类型为备考
        Map<String, Long> idNameMap = globalCache.getIdByName(Lists.newArrayList(Constants.DEFAULT_STAT_VIDEO_TYPE)
                , Lists.newArrayList(DictCodeEnum.VIDEO_TYPE.getCode()));
        LambdaQueryWrapper<UserListen> userListenQuery = new LambdaQueryWrapper<>();
        userListenQuery.select(UserListen::getId, UserListen::getListeningStatus, UserListen::getVideoId)
                .eq(UserListen::getUserId, userId)
                .eq(UserListen::getVideoType, idNameMap.get(Constants.DEFAULT_STAT_VIDEO_TYPE))
                .eq(UserListen::getCourseId, courseId).eq(UserListen::getOrderItemId, orderItemId)
                .in(ObjectUtil.isNotNull(courseAssocResourceStat)
                        && CollectionUtil.isNotEmpty(courseAssocResourceStat.getResourceIdList())
                        , UserListen::getVideoId, courseAssocResourceStat.getResourceIdList());
        List<UserListen> userListenList = userListenService.list(userListenQuery);
        if (CollectionUtil.isNotEmpty(userListenList)) {
            // 课程下的课时数
            if (ObjectUtil.isNull(courseAssocResourceStat)) {
                long courseLessonCount = userListenList.stream().map(UserListen::getVideoId).distinct().count();
                userCourseRes.setLessonCount((int) courseLessonCount);
            }
            userCourseRes.setLessonCount(courseAssocResourceStat.getResourceCount());
            long finishLessonCount = userListenList.stream()
                    .filter(e -> Constants.VIDEO_END_FLAG.equals(e.getListeningStatus()))
                    .map(UserListen::getVideoId).distinct().count();
            userCourseRes.setFinishLessonCount((int) finishLessonCount);
        }
        userCourseRes.setFreeFlag(goodSku.getFreeFlag());
        userCourseRes.setMockBankId(getMockBankId(courseRes.getCategoryId()));
        return userCourseRes;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean userCourseStudyRecordSave(List<OrderItemDo> itemList) {
        if (CollectionUtil.isEmpty(itemList)) {
            return false;
        }
        // 用户学习记录插库
        List<UserListen> userListenList = Lists.newArrayList();
        List<UserExercise> userExerciseList = Lists.newArrayList();
        itemList.forEach(orderItem -> {
            List<CourseAssocResourceStatRes> courseAssocResourceStatList = courseBizService.getCourseAssocResourceStatList(orderItem.getCourseIdList());
            courseAssocResourceStatList.forEach(courseAssocResourceStat -> userStudyRecordDefaultSave(userListenList, userExerciseList, courseAssocResourceStat, orderItem));
        });

        // 给练习记录/学习记录元素去重
        Pair<List<UserExercise>, List<UserListen>> userStudyPair = notRepeatList(userExerciseList, userListenList);
        // 避免一次性插入太多数据
        if (CollectionUtil.isNotEmpty(userStudyPair.getRight())) {
            Lists.partition(userStudyPair.getRight(), 100).forEach(ul -> userListenService.saveBatch(ul));
        }
        if (CollectionUtil.isNotEmpty(userStudyPair.getLeft())) {
            Lists.partition(userStudyPair.getLeft(), 100).forEach(ue -> userExerciseService.saveBatch(ue));
        }
        return true;
    }

    @Override
    public boolean userCourseGenerateOutSystemAccount(List<OrderItemDo> itemList) {
        if (CollectionUtil.isEmpty(itemList)) {
            return true;
        }
        Set<Long> goodsSet = itemList.stream().map(OrderItemDo::getGoodsId).collect(Collectors.toSet());
        List<Goods> orderGoodsList = goodsService.listByIds(goodsSet);
        Set<Long> userIdSet = itemList.stream().map(OrderItemDo::getBuyerId).filter(Objects::nonNull).collect(Collectors.toSet());
        Map<Long, User> userMap = userExtendService.getUserMapByIdList(userIdSet);
        Map<Long, Goods> goodsMap = orderGoodsList.stream().collect(Collectors.toMap(Goods::getId, Function.identity()));
        List<OutStudentDTO> outStudentList = Lists.newArrayList();
        Map<Long, Map<Long, List<OrderItemDo>>> mobileGoodsGroupMap = itemList.stream().collect(Collectors.groupingBy(OrderItemDo::getBuyerId, Collectors.groupingBy(OrderItemDo::getGoodsId)));
        mobileGoodsGroupMap.forEach((userId, orderGoodsMap) -> {
            OutStudentDTO outStudentDTO = new OutStudentDTO();
            User orderUser = userMap.getOrDefault(userId, new User());
            outStudentDTO.setPhone(orderUser.getMobile());
            outStudentDTO.setName(StrUtil.isNotBlank(orderUser.getName()) ? orderUser.getName() : orderUser.getNickName());
            processGoodsCourse(orderGoodsMap, goodsMap, outStudentDTO);
            outStudentList.add(outStudentDTO);
        });
        // 是否有题库
        boolean hasOutBank = outStudentList.stream().filter(e -> CollectionUtil.isNotEmpty(e.getOutCourseList()))
                .flatMap(e -> e.getOutCourseList().stream())
                .allMatch(e -> ObjectUtil.isNotNull(e) && CollectionUtil.isNotEmpty(e.getOutCourseIdList()));
        // 该订单下没有关联的题库 不需要调用建培圈接口
        if (!hasOutBank) {
            return true;
        }
        return trainApiClient.generateTrainStudent(outStudentList);
    }

    private void processGoodsCourse(Map<Long, List<OrderItemDo>> orderGoodsMap, Map<Long, Goods> goodsMap, OutStudentDTO outStudentDTO) {
        if (CollectionUtil.isEmpty(orderGoodsMap)) {
            return;
        }
        List<OutStudentCourseDTO> outCourseList = Lists.newArrayList();
        orderGoodsMap.forEach((goodsId, itemList) -> {
            Set<Long> itemCourseIdSet = itemList.stream().filter(e -> CollectionUtil.isNotEmpty(e.getCourseIdList())).flatMap(e -> e.getCourseIdList().stream()).collect(Collectors.toSet());
            List<CourseAssocResourceStatRes> resourceStatList = courseBizService.getCourseAssocResourceStatList(itemCourseIdSet);
            Set<Long> orderBankIdSet = Sets.newHashSet();
            resourceStatList.forEach(resource -> resource.getResourceListByTypeMap().forEach((resourceType, resourceList) -> {
                if (CourseAssocResourceTypeEnum.QUESTION.getCode().equals(resourceType)) {
                    resourceList.forEach(res -> {
                        if (ObjectUtil.isNotNull(res.getId())) {
                            orderBankIdSet.add(res.getId());
                        }
                    });
                }
                resourceList.forEach(res -> {
                    if (ObjectUtil.isNotNull(res.getId())) {
                        orderBankIdSet.add(res.getBankId());
                    }
                });
            }));

            OutStudentCourseDTO studentCourseDTO = new OutStudentCourseDTO();
            Optional.ofNullable(goodsMap.get(goodsId)).ifPresent(goods -> {
                studentCourseDTO.setGoodsStartDate(goods.getValidStartTime());
                studentCourseDTO.setGoodsEndDate(goods.getValidEndTime());
            });

            if (CollectionUtil.isEmpty(orderBankIdSet)) {
               return;
            }
            List<ResourceQuestionBank> resourceQuestionBankList = questionBankExtendService.listByIdList(orderBankIdSet);
            Set<Long> outSystemCourseIdSet = resourceQuestionBankList.stream().map(ResourceQuestionBank::getAssoCourseId).collect(Collectors.toSet());
            if (CollectionUtil.isNotEmpty(outSystemCourseIdSet)) {
                studentCourseDTO.setOutCourseIdList(Lists.newArrayList(outSystemCourseIdSet));
            }
            Set<Long> outSystemBankIdSet = resourceQuestionBankList.stream().map(ResourceQuestionBank::getAssoBankId).collect(Collectors.toSet());
            if (CollectionUtil.isNotEmpty(outSystemBankIdSet)) {
                studentCourseDTO.setOutBankIdList(Lists.newArrayList(outSystemBankIdSet));
            }
            outCourseList.add(studentCourseDTO);
        });
        outStudentDTO.setOutCourseList(outCourseList);
    }

    @Override
    public ApiCustomerUserStudyReportRes courseStudyReport(ApiCustomerUserCourseBaseReq req) {
        Long defaultVideoTypeId = globalCache.getIdBySimpleName(Constants.DEFAULT_STAT_VIDEO_TYPE, DictCodeEnum.VIDEO_TYPE.getCode());
        if (ObjectUtil.isNull(defaultVideoTypeId)) {
            throw new GlobalException("默认视频备考类型字典值不存在");
        }
        ApiCustomerUserStudyReportRes res = new ApiCustomerUserStudyReportRes();
        CourseAssocResourceStat courseAssocResourceStat = courseExtendService.getCourseAssocResourceStat(req.getCourseId(), CourseAssocResourceTypeEnum.VIDEO);
        if (ObjectUtil.isNull(courseAssocResourceStat)) {
            throw new GlobalException("课程统计数据有误,请稍后再试");
        }
        // 用户听课的时间集合
        Optional<UserListen> userListenMaxDateOpt = processUserListen(req, defaultVideoTypeId, res, courseAssocResourceStat);

        // 用户练习的时间集合
        Optional<UserExercise> userExerciseMaxDateOpt = processUserExercise(req, res);
        Date studyStartTime = null;
        Date studyEndTime = null;
        // 用户听课时间 和 用户练习时间 求并集计算天数
        if (userExerciseMaxDateOpt.isPresent()) {
            studyStartTime = userExerciseMaxDateOpt.get().getStartStudyTime();
            studyEndTime = userExerciseMaxDateOpt.get().getEndStudyTime();
        }
        if (userListenMaxDateOpt.isPresent()) {
            Date startStudyListenTime = userListenMaxDateOpt.get().getStartStudyTime();
            Date endStudyListenTime = userListenMaxDateOpt.get().getEndStudyTime();
            if (ObjectUtil.isEmpty(studyStartTime)) {
                studyStartTime = startStudyListenTime;
            } else {
                studyStartTime =  DateUtil.compare(studyStartTime, startStudyListenTime) <= 0
                        ? studyStartTime : ObjectUtil.isNotEmpty(startStudyListenTime)
                        ? startStudyListenTime : studyStartTime;
            }
            if (ObjectUtil.isEmpty(studyEndTime)) {
                studyEndTime = endStudyListenTime;
            } else {
                studyEndTime = DateUtil.compare(studyEndTime, endStudyListenTime) >= 0 ? studyEndTime : endStudyListenTime;
            }
        }
        res.setStudyDay(0);
        if (ObjectUtil.isNotEmpty(studyStartTime) && ObjectUtil.isNotEmpty(studyEndTime)) {
            List<DateTime> rangedToList = DateUtil.rangeToList(studyStartTime, studyEndTime, DateField.DAY_OF_YEAR);
            res.setStudyDay(rangedToList.size());
        }
        return res;
    }



    @Override
    public boolean addCourseVideo(CourseVideoReq req) {
        List<ResourceCourseVideoPo> resourceCourseVideoPoList = Lists.newArrayList();
        if (CollectionUtil.isNotEmpty(req.getVideoIdList())) {
             resourceCourseVideoPoList = videoExtendService.listByIdList(req.getVideoIdList());
            if (CollectionUtil.isEmpty(resourceCourseVideoPoList)) {
                return false;
            }
            resourceCourseVideoPoList.sort(Comparator.comparing(ResourceCourseVideoPo::getOrderNum));
        }
        Map<Long, ResourceCourseLivePo> liveMap = Maps.newHashMap();
        // 重置回放操作
        if (Constants.RESET_LIVE_COURSE.equals(req.getOperateVideoFlag())) {
             List<ResourceCourseLivePo> resourceCourseLivePoList = liveService.listPoByDo(new ResourceCourseLiveDo());
            if (CollectionUtil.isNotEmpty(resourceCourseLivePoList)) {
                liveMap = resourceCourseLivePoList.stream().collect(Collectors.toMap(ResourceCourseLivePo::getId, Function.identity()));
            }
        }
        LinkedHashMap<Long, ResourceCourseVideoPo> videoMap = resourceCourseVideoPoList.stream().collect(Collectors.toMap(ResourceCourseVideoPo::getId, Function.identity(), (existing, replacement) -> existing,
                LinkedHashMap::new));
        Map<Long, ResourceCourseLivePo> finalLiveMap = liveMap;
        req.getCourseIdList().forEach(courseId -> processCourseVideo(courseId, videoMap, finalLiveMap, req.getOperateVideoFlag(), req.getCatalog()));
        return true;
    }



    @Override
    public ApiUserCourseStudyRecordRes getUserCourseStudyRecord(Long userId, Long courseId, Long orderItemId) {
        // 用户学习记录 视频-题库
        ApiUserCourseStudyRecordRes userCourseStudyRecordRes = new ApiUserCourseStudyRecordRes();
        // 统计视频类型-备考
        List<UserListen> userListenList = getUserStudyListByUser(CourseAssocResourceTypeEnum.VIDEO
                , () -> userListenService.listByUserCourse(userId, courseId, orderItemId, null, Lists.newArrayList()));
        Map<Long, UserListen> userListenVideoIdMap = userListenList.stream()
                .collect(Collectors.groupingBy(
                        UserListen::getVideoId,
                        Collectors.collectingAndThen(
                                Collectors.maxBy(Comparator.comparing(UserListen::getListeningStatus
                                        , Comparator.nullsFirst(Comparator.naturalOrder()))),
                                max -> max.orElse(null)
                        )
                ));
        // 统计题库类型-备考
        List<UserExercise> userExerciseList = getUserStudyListByUser(CourseAssocResourceTypeEnum.QUESTION
                , () -> userExerciseService.listByUserCourse(userId, courseId, orderItemId, null));
        Map<Long, UserExercise> userExerciseBankIdMap = userExerciseList.stream()
                .collect(Collectors.groupingBy(
                        UserExercise::getBankId,
                        Collectors.collectingAndThen(
                                Collectors.maxBy(Comparator.comparing((UserExercise ue) ->
                                                SysConst.Flag.IS.getFlag().equals(ue.getExerciseStatus())
                                                        ? SysConst.Flag.IS.getFlag() : SysConst.Flag.NO.getFlag())
                                        .thenComparing(UserExercise::getCorrectCount
                                                , Comparator.nullsFirst(Comparator.naturalOrder()))),
                                max -> max.orElse(null)
                        )
                ));
        userCourseStudyRecordRes.setUserExerciseMap(userExerciseBankIdMap);
        userCourseStudyRecordRes.setUserListenMap(userListenVideoIdMap);
        return userCourseStudyRecordRes;
    }

    /**
     * 处理用户练习数据
     * @param req
     * @param res
     * @return
     */
    private Optional<UserExercise> processUserExercise(ApiCustomerUserCourseBaseReq req, ApiCustomerUserStudyReportRes res) {
        Long defaultBankTypeId = globalCache.getIdBySimpleName(Constants.DEFAULT_STAT_BANK_TYPE, DictCodeEnum.BANK_TYPE.getCode());
        if (ObjectUtil.isNull(defaultBankTypeId)) {
            throw new GlobalException("默认题库备考类型字典值不存在");
        }
        Optional<UserExercise> userExerciseMaxDateOpt = Optional.empty();
        // 该用户课程下所有的练习记录
        List<UserExercise> userExerciseList = userExerciseService.listByUserCourse(req.getUserId(), req.getCourseId(), req.getOrderItemId(), null);
        if (CollectionUtil.isNotEmpty(userExerciseList)) {
            Triple<Integer, Optional<Integer>, Optional<Integer>> userCourseExerciseTripe = userExerciseList.stream().collect(Collectors.collectingAndThen(
                    Collectors.toList(),
                    list -> {
                        Map<Long, UserExercise> userExerciseBankIdMap = list.stream().filter(e -> defaultBankTypeId.equals(e.getBankType()))
                                .collect(Collectors.groupingBy(
                                        UserExercise::getBankId,
                                        Collectors.collectingAndThen(
                                                Collectors.maxBy(Comparator.comparing(UserExercise::getExerciseCount, Comparator.nullsFirst(Comparator.naturalOrder()))),
                                                max -> max.orElse(null)
                                        )
                                ));
                        // 用户随堂练习数量
                        Optional<Integer> userClassPracticeExerciseSumOptional = userExerciseBankIdMap.values().stream().filter(ObjectUtil::isNotNull).map(UserExercise::getExerciseCount).reduce(Integer::sum);
                        // 用户总练题数 包含真题等
                        Optional<Integer> userTotalExerciseQuestionCount = list.stream().map(UserExercise::getExerciseCount).filter(Objects::nonNull).reduce(Integer::sum);
                        Map<Long, UserExercise> bankIdUserExerciseMap = list.stream().filter(e -> defaultBankTypeId.equals(e.getBankType())).collect(Collectors.toMap(UserExercise::getBankId, userExercise -> userExercise, (existing, replacement) -> existing));
                        // 题库总数之和随堂练习题库
                        int totalQuestionCount = bankIdUserExerciseMap.values().stream().filter(Objects::nonNull).mapToInt(UserExercise::getQuestionCount).sum();
                        return Triple.of(totalQuestionCount, userTotalExerciseQuestionCount, userClassPracticeExerciseSumOptional);
                    }
            ));
            res.setTotalQuestionCount(userCourseExerciseTripe.getLeft());
            userCourseExerciseTripe.getMiddle().ifPresent(res::setTotalExerciseCount);
            userCourseExerciseTripe.getRight().ifPresent(count -> {
                if (count > userCourseExerciseTripe.getLeft()) {
                    res.setExerciseCount(userCourseExerciseTripe.getLeft());
                }else {
                    res.setExerciseCount(count);
                }
            });

            userExerciseMaxDateOpt = userExerciseList.stream().max(Comparator.comparing(UserExercise::getEndStudyTime, Comparator.nullsFirst(Comparator.naturalOrder())));
        }
        return userExerciseMaxDateOpt;
    }

    /**
     * 处理用户听课数据
     * @param req
     * @param defaultVideoTypeId
     * @param res
     * @param courseAssocResourceStat
     * @return
     */
    private Optional<UserListen> processUserListen(ApiCustomerUserCourseBaseReq req, Long defaultVideoTypeId, ApiCustomerUserStudyReportRes res, CourseAssocResourceStat courseAssocResourceStat) {
        Optional<UserListen> userListenMaxDateOpt = Optional.empty();
        // 该用户课程下所有的学习课时记录 默认备考类型视频
        List<UserListen> userListenList = userListenService.listByUserCourse(req.getUserId(), req.getCourseId(), req.getOrderItemId(), defaultVideoTypeId, courseAssocResourceStat.getResourceIdList());
        if (CollectionUtil.isNotEmpty(userListenList)) {
            // 用户课时统计 总共课时,已学习课时,已学习时长
            Pair<Set<Long>, Set<Long>> userCourseListenPair = userListenList.stream().collect(Collectors.collectingAndThen(
                    Collectors.toList(),
                    list -> {
                        Set<Long> courseVideoSet = list.stream().map(UserListen::getVideoId).collect(Collectors.toSet());
                        Set<Long> userListenVideoSet = list.stream().filter(e -> Constants.VIDEO_END_FLAG.equals(e.getListeningStatus())).map(UserListen::getVideoId).collect(Collectors.toSet());
                        return Pair.of(userListenVideoSet, courseVideoSet);
                    }
            ));
            // 用户听课时长统计,每个视频的最大听课时间 叠加
            Map<Long, UserListen> userListenMaxDurationMap = userListenList.stream().collect(Collectors.groupingBy(
                    UserListen::getVideoId,
                    Collectors.collectingAndThen(
                            Collectors.maxBy(Comparator.comparing(UserListen::getListenedDuration, Comparator.nullsFirst(Comparator.naturalOrder()))),
                            max -> max.orElse(null)
                    )
            ));
            Optional<Long> userMaxListenDurationOpt = userListenMaxDurationMap.values().stream().map(UserListen::getListenedDuration).filter(Objects::nonNull).reduce(Long::sum);
            res.setListenedCount(userCourseListenPair.getLeft().size());
            res.setTotalLessonVideoCount(courseAssocResourceStat.getResourceCount());
            userMaxListenDurationOpt.ifPresent(res::setListenDuration);

            // 用户听课时间列表
            userListenMaxDateOpt = userListenList.stream().max(Comparator.comparing(UserListen::getEndStudyTime, Comparator.nullsFirst(Comparator.naturalOrder())));
        }
        return userListenMaxDateOpt;
    }


    /**
     * 用户下单后默认学习记录保存
     * @param userListenList 用户听课记录
     * @param userExerciseList 用户练习记录
     * @param courseAssocResourceStat 用户课程相关的资源 视频/题库
     * @param orderItem 订单
     */
    private void userStudyRecordDefaultSave(List<UserListen> userListenList, List<UserExercise> userExerciseList, CourseAssocResourceStatRes courseAssocResourceStat, OrderItemDo orderItem){
        Map<String, List<? extends ResourceCourseBase>> resourceListByTypeMap = courseAssocResourceStat.getResourceListByTypeMap();
        resourceListByTypeMap.forEach((resourceType, resourceList) -> {
            if (CollectionUtil.isEmpty(resourceList)) {
                return;
            }
            // 避免重复生成同一条练习记录
            List<Long> repeatIdList = Lists.newArrayList();
            resourceList.forEach(resource -> {
                if (CourseAssocResourceTypeEnum.CAN_ASSOC_BANK_TYPE_LIST.contains(resourceType)) {
                    if (ObjectUtil.isNotNull(resource.getBankId())) {
                        if (!repeatIdList.contains(resource.getBankId())) {
                            UserExercise userExercise = new UserExercise();
                            userExercise.setUserId(orderItem.getBuyerId());
                            userExercise.setCourseId(courseAssocResourceStat.getCourseId());
                            userExercise.setOrderItemId(orderItem.getId());
                            userExercise.setBankId(resource.getBankId());
                            userExercise.setBankType(resource.getBankType());
                            userExercise.setQuestionCount(resource.getQuestionCount());
                            userExerciseList.add(userExercise);
                            repeatIdList.add(resource.getBankId());
                        }

                    }
                }
                if (CourseAssocResourceTypeEnum.VIDEO.getCode().equals(resourceType)) {
                    UserListen userListen = new UserListen();
                    userListen.setUserId(orderItem.getBuyerId());
                    userListen.setCourseId(courseAssocResourceStat.getCourseId());
                    userListen.setOrderItemId(orderItem.getId());
                    userListen.setVideoId(resource.getId());
                    userListen.setVideoType(resource.getType());
                    userListen.setVideoDuration(resource.getResourceCount());
                    userListenList.add(userListen);
                }

            });
        });
    }

    /**
     * 默认查询用户的学习情况 视频类型-备考 题库类型-评测
     * @param courseResourceEnum 资源类型
     * @param func 函数接口
     * @return 资源列表
     */
    private <T> List<T> getUserStudyListByUser(CourseAssocResourceTypeEnum courseResourceEnum, Supplier<List<T>> func){
        /*String defaultResourceName, defaultResourceDictCode;
        if (CourseAssocResourceTypeEnum.VIDEO == courseResourceEnum) {
            defaultResourceName = Constants.DEFAULT_STAT_VIDEO_TYPE;
            defaultResourceDictCode = DictCodeEnum.VIDEO_TYPE.getCode();
        }else {
            defaultResourceName = Constants.DEFAULT_STAT_BANK_TYPE;
            defaultResourceDictCode = DictCodeEnum.BANK_TYPE.getCode();
        }
        // 默认统计题库/视频类型为备考
        Map<String, Long> idName = globalCache.getIdByName(Lists.newArrayList(defaultResourceName), Lists.newArrayList(defaultResourceDictCode));
        Long defaultResourceType = idName.get(defaultResourceName);
        if (ObjectUtil.isNull(defaultResourceType)) {
            throw new GlobalException("默认" + defaultResourceName + "类型字典值不存在");
        }*/
        return func.get();
//        if (ObjectUtil.isNotNull(defaultResourceType)) {
//            return func.get();
//        }
//        return Lists.newArrayList();
    }

    private void processCourseVideo(Long courseId, Map<Long, ResourceCourseVideoPo> videoMap
            , Map<Long, ResourceCourseLivePo> liveMap, Integer operateVideoFlag, String catalog){
        CoursePo course = courseExtendService.getPoByIdNotNull(courseId);
        List<CourseStructure> structureJsonList = course.getStructureJson();
        structureJsonList = BeanUtil.copyToList(structureJsonList, CourseStructure.class);
        if (StrUtil.isNotBlank(catalog)) {
            // 添加课程操作不需要结构不为空
            if (CollectionUtil.isNotEmpty(structureJsonList)) {
                structureJsonList.stream().filter(e -> StrUtil.equals(e.getName(), catalog)).findFirst().ifPresent(courseStructure -> {
                    if (Constants.ADD_COURSE.equals(operateVideoFlag) || CollectionUtil.isNotEmpty(courseStructure.getChildren())) {
                        List<CourseStructure> newCourseStructureList = Lists.newArrayList();
                        addVideoCourseStruct(newCourseStructureList, courseStructure.getChildren()
                                , liveMap, operateVideoFlag, videoMap, courseStructure.getId());
                        courseStructure.setChildren(newCourseStructureList);
                    }
                });
            }
        }
        course.setStructureJson(structureJsonList);
        User obj = new User();
        obj.setId(1L);
        ApiSessionHolder.setSessionObj(obj);
        courseExtendService.saveStatCourseAssocResource(course, structureJsonList);
    }

    private void addVideoCourseStruct(List<CourseStructure> newCourseStructureList
            , List<CourseStructure> courseChildStructureList,
                                      Map<Long, ResourceCourseLivePo> liveMap,
                                      Integer operateVideoFlag,
                                      Map<Long, ResourceCourseVideoPo> videoMap, Long parentId){
        if (Constants.RESET_LIVE_COURSE.equals(operateVideoFlag)) {
            // 替换课程
            courseChildStructureList.forEach(courseStructure -> {
                if (liveMap.containsKey(courseStructure.getTypeAssoId())) {
                    ResourceCourseLivePo courseLive = liveMap.get(courseStructure.getTypeAssoId());
                    courseStructure.setName(courseLive.getName());
                    courseStructure.setDocId(courseLive.getDocId());
                    courseStructure.setDocName(courseLive.getDocName());
                    courseStructure.setQuestionId(courseLive.getBankId());
                    courseStructure.setQuestionName(courseLive.getBankName());
                }
                newCourseStructureList.add(courseStructure);
            });
            return;
        }
        // 减少课程
        if (Constants.DECR_COURSE.equals(operateVideoFlag)) {
            courseChildStructureList.forEach(courseStructure -> {
                if (!videoMap.containsKey(courseStructure.getTypeAssoId())) {
                    newCourseStructureList.add(courseStructure);
                }
            });
        } else if (Constants.ADD_COURSE.equals(operateVideoFlag)){
            // 如果原先有课程先把原先课程加入进来
            if (CollectionUtil.isNotEmpty(courseChildStructureList)) {
                //增加课程
                newCourseStructureList.addAll(courseChildStructureList);
            }
            videoMap.forEach((videoId, resourceVideo) -> {
                CourseStructure courseStructure = new CourseStructure();
                courseStructure.setId(IdUtil.getSnowflakeNextId());
                courseStructure.setTypeAssoId(videoId);
                courseStructure.setName(resourceVideo.getName());
                courseStructure.setType(CourseAssocResourceTypeEnum.VIDEO.getCode());
                courseStructure.setDocId(resourceVideo.getDocId());
                courseStructure.setDocName(resourceVideo.getDocName());
                courseStructure.setQuestionId(resourceVideo.getBankId());
                courseStructure.setQuestionName(resourceVideo.getBankName());
                courseStructure.setParentId(parentId);
                newCourseStructureList.add(courseStructure);
            });
        }else {
            // 替换课程
            courseChildStructureList.forEach(courseStructure -> {
                if (videoMap.containsKey(courseStructure.getTypeAssoId())) {
                    ResourceCourseVideoPo courseVideo = videoMap.get(courseStructure.getTypeAssoId());
                    courseStructure.setName(courseVideo.getName());
                    courseStructure.setDocId(courseVideo.getDocId());
                    courseStructure.setDocName(courseVideo.getDocName());
                    courseStructure.setQuestionId(courseVideo.getBankId());
                    courseStructure.setQuestionName(courseVideo.getBankName());
                }
                newCourseStructureList.add(courseStructure);
            });
        }
    }


    /**
     * 对学习记录元素去重 练习/视频
     * @param userExerciseList
     * @param userListenList
     */
    private Pair<List<UserExercise>, List<UserListen>> notRepeatList(List<UserExercise> userExerciseList, List<UserListen> userListenList) {
        Map<String, UserExercise> userExerciseMap = userExerciseList.stream()
                .collect(Collectors.toMap(
                        userExercise -> StrUtil.format("{}_{}_{}_{}", Objects.toString(userExercise.getUserId(), "")
                                ,  Objects.toString(userExercise.getCourseId(), "")
                                , Objects.toString(userExercise.getOrderItemId(), "")
                                , Objects.toString(userExercise.getBankId(), "")),
                        Function.identity(),
                        (existing, replacement) -> existing
                ));
        if (CollectionUtil.isEmpty(userExerciseMap.values())) {
            userExerciseList = Lists.newArrayList();
        } else {
            userExerciseList = Lists.newArrayList(userExerciseMap.values());
        }

        Map<String, UserListen> userListenMap = userListenList.stream()
                .collect(Collectors.toMap(
                        userListen -> StrUtil.format("{}_{}_{}_{}", Objects.toString(userListen.getUserId(), "")
                                ,  Objects.toString(userListen.getCourseId(), "")
                                , Objects.toString(userListen.getOrderItemId(), "")
                                , Objects.toString(userListen.getVideoId(), "")),
                        Function.identity(),
                        (existing, replacement) -> existing
                ));
        if (CollectionUtil.isEmpty(userListenMap.values())) {
            userListenList = Lists.newArrayList();
        } else {
            userListenList = Lists.newArrayList(userListenMap.values());
        }
        return Pair.of(userExerciseList, userListenList);
    }

    /**
     * 通过课程类型获取模拟题库id
     * @param courseType
     * @return
     */
    private Long getMockBankId(Long courseType){
        // 题库类型为练习的题库作为模拟题库
        Map<String, Long> idNameMap = globalCache.getIdByName(Lists.newArrayList(Constants.EXERCISE_TEST_BANK_TYPE)
                , Lists.newArrayList(DictCodeEnum.BANK_TYPE.getCode()));
        // 只有上架的题库才能练习
        LambdaQueryWrapper<ResourceQuestionBank> mockBankQuery = Wrappers.<ResourceQuestionBank>lambdaQuery()
                .eq(ResourceQuestionBank::getType, idNameMap.get(Constants.EXERCISE_TEST_BANK_TYPE))
                .eq(ResourceQuestionBank::getCourseType, courseType)
                .eq(ResourceQuestionBank::getPublishedFlag, SysConst.Flag.IS.getFlag())
                .last("LIMIT 1");
        ResourceQuestionBank mockBank = questionBankExtendService.getBaseService().getOne(mockBankQuery);
        if (ObjectUtil.isNull(mockBank)) {
            return null;
        }
        return mockBank.getId();
    }
}
